package capstone.tagit;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.MyLocationOverlay;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;


/**
 * 
 * The application's primary Activity.  Shows the map and contains 2 overlays.
 * Overlay TagsList : Displays the tags
 * Overlay LongPressOverlay : attempts to detect longpresses, which then creates a tag at that location
 * Analyzes the configurable files and takes in the specified options.
 * Handles reads and writes to and from the local file, operations surrounding server communication,
 * 		and the creation of new tag objects to be added onscreen.
 * This class may have too many responsibilites, and should perhaps be split into smaller ones,
 * with more clearly defined responsibilities.
 *
 */
public class TagItMapActivity extends MapActivity {
	/**
	 * A pointer to this object.  Used to grant subclasses access to the TagItMapActivity
	 */
	TagItMapActivity parent;
	/**
	 * Used primarily for logging messages
	 */
	String ACTNAME = "TagIt!";
	/**
	 * The actual map itself.  Uses Google's API's.
	 * Must be invalidated when new tags added so that it then refreshes.
	 */
	MapView mapView;	//Map handler
	/**
	 * A LocationManager to deal with location setup.  Used to turn on and off the listener.
	 */
	LocationManager locationManager;
	/**
	 * A Location Listener to listen for location changes.
	 */
	LocationListener locationListener;
	/**
	 * controls the MapView.  Currently used only to set the initial zoom level.
	 */
	MapController mapControl;
	/**
	 * A TagsList Overlay that contains the Tags to be displayed on screen.
	 */
	TagsList tagsOnScreen;
	/**
	 * An Overlay to Listen for longpresses.  Currently does not work terribly well,
	 * as it is too sensitive to movement.
	 */
	MyLocationOverlay myLocationOverlay;
	/**
	 * A list of Overlays currently on top of the MapView.  Contains tagsList and myLocationOverlay.
	 */
	List<Overlay> mapOverlays;
	/**
	 * A list of TagTypes available for this app.
	 */
	ArrayList<TagType> tagTypes = new ArrayList<TagType>();
	/**
	 * A list of Strings corresponding to TagTypes.  Used to feed these strings to the other Activities.
	 */
	ArrayList<String> typeNames = new ArrayList<String>();
	/**
	 * A list of Strings corresponding to the configurable Attributes available in the app.
	 */
	ArrayList<String> attributes;
	/**
	 * A list of Strings to hold all downloaded data from server
	 */
	ArrayList<String> tagBuffer;
	/**
	 * A list of Tags which have not yet been uploaded to the server.  Gets filled by adding tags,
	 * emptied by uploading to server.
	 * Note: if this app is force closed, this list will be lost, yet not uploaded to server.  The app
	 * tags will remain out of synch with the server.
	 */
	LinkedList<Tag> tagsForServer;
	/**
	 *Temporarily hard coded variable for passing to the "getTagsFromServer" method.
	 *Change this to read from a resource xml later.
	 *Measured in miles
	 */
	int radius = 24000;
	
	/**
	 * The current location.  Updated in the LocationListener.  Testing on one device gave the 
	 * impression that onLocationChanged was not being called, so currentLocationPoint is also updated
	 * in the methods where it is needed.
	 */
	GeoPoint currentLocationPoint;
	/**
	 * A point corresponding to a location where a user applies a long press.  Set when a long press is detected.
	 */
	GeoPoint pressLocationPoint;
	/**
	 * A menuSettings object shared between the three activities.  Contains information about
	 * GPS status, the current tagging color, and tags to be displayed onscreen.
	 */
	SharedPreferences menuSettings;
	/**
	 * An Editor to grant update access to menuSettings.
	 */
	SharedPreferences.Editor prefEditor;
	/**
	 * The current TagType.  This is used so that the app knows what type to make a new
	 * tag when it creates one.
	 */
	TagType currentType;
	/**
	 * A pointer to a file where tags are stored locally.  This should be removed if the
	 * app is updated to store tags in a database.
	 */
	String TagFileName = "tagfile";
	/**
	 * SimpleDateFormat allows dates to be displayed in a certain format.
	 */
	SimpleDateFormat dateFormat;
	/**
	 * A String corresponding to the server the app should communicate with.
	 */
	String serverAddress;
	/**
	 * A Resource object used to aid in acquiring resources specified in locations
	 * such as xml or drawable.
	 */
	Resources resource;
	@Override
	/**
	 * The method called on Activation startup.  Instantiates the fields, sets up the GPS listener.
	 * Configures the onscreen buttons.  Gets TagTypes and Attributes from xml files.
	 * 
	 * @param savedInstanceState A Bundle containing saved information that could
	 * be used to aid in reinstantiation.  Not currently used.
	 */
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		parent = this;
		resource = getResources();
		locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
		locationListener = new myLocationListener();
		locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000,10,locationListener);
		//Button stuff
		Button tagButton = (Button)findViewById(R.id.Button4);
		tagButton.setOnClickListener(tagListener);
		Button menu = (Button) findViewById(R.id.Button1);
		menu.setOnClickListener(menuListener);
		Button refreshButton = (Button)findViewById(R.id.refreshButton);
		refreshButton.setOnClickListener(refreshListener);
		//
		menuSettings = getSharedPreferences("MenuPref", MODE_PRIVATE);
		prefEditor = menuSettings.edit();
		prefEditor.putBoolean("GPS_STATUS",true);
		prefEditor.commit();
		attributes = new ArrayList<String>();
		dateFormat = new SimpleDateFormat(resource.getString(R.string.Date));
		tagsForServer = new LinkedList<Tag>();
		getTagTypes();
		getAttributesFromFile();
		initMap();
		serverAddress = resource.getString(R.string.serveraddress);//grab server address from server.xml 
		//maybe move all that to one settings file
		Log.v(ACTNAME,"Done getting tags from server");
		getTagsFromFile();
		
	}
	/**
	 * Gets the Tag Types and the corresponding images from the tagtypes.xml file.  Saves the types in
	 * tagTypes and tagNames.  Also sets the Bounds for each marker.  This is necessary to make them all
	 * display correctly.
	 */
	private void getTagTypes(){
		String[] typesFromFile = resource.getStringArray(R.array.type_array);
		TypedArray pictures = resource.obtainTypedArray(R.array.pic_array);
		for (int i = 0; i < typesFromFile.length; i++){
			String name = typesFromFile[i];
			Drawable marker = pictures.getDrawable(i);
			
			int width = marker.getIntrinsicWidth();
			int height = marker.getIntrinsicHeight();
			marker.setBounds(-width/2, -height, width/2, 0);
			
			tagTypes.add(new TagType(name, marker));
			typeNames.add(name);
			prefEditor.putBoolean(name,true);			
		}
		prefEditor.putInt("COLOR_CHOICE",0);
		currentType = tagTypes.get(0);
		prefEditor.commit();
		//		}catch(IOException e){}
	}
	/**
	 * Gets the attribute types from the attributes.xml file.  Adds each attribute into the attributes list.
	 */
	private void getAttributesFromFile(){
		String[] attribute_names = resource.getStringArray(R.array.attribute_names);
		for (String attribute : attribute_names){
			attributes.add(attribute);
		}
	}
	
	/**
	 * Gets the tags from the local file.  Adds them to local program memory.  Dupliate prevention is handled in TagsList.
	 */
	private void getTagsFromFile(){
		InputStream tagsFile = null;
		try{
			tagsFile = openFileInput(TagFileName);
		}catch(FileNotFoundException e){
			Log.e("getTagsFromFile",e.toString());
			return;
		}
		BufferedReader readTags = new BufferedReader(new InputStreamReader(tagsFile));
		try{
			String tagLine;
			while((tagLine = readTags.readLine()) != null){
				
				Tag overlayItem = Tag.parseStringToTag(tagLine,tagTypes,attributes,dateFormat);
				if (overlayItem != null){
					addSeveralItems(Tag.parseStringToTag(tagLine, tagTypes, attributes,dateFormat));
				}
			}
			mapView.invalidate();
		}catch(Exception e){
			Log.e("getTagsFromFile",e.toString());
		}
	}

/**
 * Returns tags from a certain location given longitude, latitude, and miles.
 * Information from parameters is stored in a NameValuePair object, encoded and sent with a HTTPClient.
 * Information received from server is stored in string buffer before being converted to tag format.
 * Queries getTags.php on the server. 
 * @param longitude An int variable for Longitude in microdegrees.
 * @param latitude An int variable for Latitude in microdegrees.
 * @param radius A double variable for Radius in miles.
 */
	private void getTagsFromServer(final int longitude, final int latitude, final double radius){
		(new Thread(){
			public void run(){ //start connection
				try{
					ArrayList<NameValuePair> nameValuePair = new ArrayList<NameValuePair>(); //necessary for Post entity
					nameValuePair.add(new BasicNameValuePair("longitude",Integer.toString(longitude)));
					nameValuePair.add(new BasicNameValuePair("latitude", Integer.toString(latitude)));
					nameValuePair.add(new BasicNameValuePair("radius",Double.toString(radius)));
					tagBuffer = new ArrayList<String>();
					HttpClient httpclient = new DefaultHttpClient();
					HttpPost httppost = new HttpPost("http://"+serverAddress+"/getTags.php");
					BufferedReader input = null;
					httppost.setEntity(new UrlEncodedFormEntity(nameValuePair));
					HttpResponse response = httpclient.execute(httppost);
					input = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
					String line = "";
					Log.v("Connection","Printing out valid information");
					while((line = input.readLine())!= null){//Read in all the stuff
						Log.v("Output Line", line.toString());
						if(line.length()>0)tagBuffer.add(line.toString());
					} 
					input.close();
					if(!tagBuffer.isEmpty()){
						Log.v("outputting tag buffer", tagBuffer.toString());
						for(String tempTag : tagBuffer){
							final Tag temp = Tag.parseStringToTag(tempTag, tagTypes, attributes,dateFormat);
							if(temp != null){
								Runnable addTag = new Thread(){
									Tag item = temp;
									public void run(){
										if(addOverlayItem(item)){
											Log.v("line 240", "adding");
											writeToFile(item);
										}
									}
								};
								parent.runOnUiThread(addTag);
							}
						}
					}
				}catch(Exception e){Log.e("catch",e.toString());
				}
			}
		}).start();
	}
	/**
	 * Sends all the tags in LinkedList tagsForServer to the server. Queries setTags.php.
	 */
	private void sendTagsToServer(){
		(new Thread(){ 
			public void run(){
				try{
						ArrayList<NameValuePair> nameValuePair = new ArrayList<NameValuePair>();
						int i = 0;
						if(!tagsForServer.isEmpty()){
						for(Tag temp : tagsForServer){		
							nameValuePair.add(new BasicNameValuePair(Integer.toString(i), temp.getInfoString(dateFormat)));
							i++;
						}
						//clear pendingTags
						//This needs to be updated so that the pending tags are only cleared after a successful connection and transmit.
						tagsForServer = new LinkedList<Tag>();
						//connecting to server
						HttpClient httpclient = new DefaultHttpClient();
						HttpPost httppost = new HttpPost("http://"+serverAddress+"/setTags.php");
						httppost.setEntity(new UrlEncodedFormEntity(nameValuePair));
						HttpResponse response = httpclient.execute(httppost);
						BufferedReader input = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
						String line = ""; 
						while((line = input.readLine())!= null){//Read in all the stuff
							Log.v("Output Line", line.toString());
						}
						input.close();
					}
				}catch(Exception e){
					Log.e("catch",e.toString());
				}
			}
		
		}).start();
	}

	/**
	 * Sets pressLocationPoint to the location corresponding to the location pressed by the user.
	 * Instantiates TaggingActivity to specify a Tag at that location.
	 * RequestCode 2 corresponds to the press location.
	 * 
	 * @param event The Long Press event.  Used to get the location of the long press.
	 */
	private void createTagAtLocation(MotionEvent event){
		Projection projection = mapView.getProjection();
		pressLocationPoint = projection.fromPixels((int)event.getX(),(int)event.getY());
		Intent myIntent = new Intent(this, TaggingActivity.class);
		myIntent.putStringArrayListExtra("attributes", attributes);
		myIntent.putStringArrayListExtra("tagTypes",typeNames);
		startActivityForResult(myIntent, 2);
	}
	/**
	 * The listener for the Tag Button.  If GPS is off, will pop up a message.
	 * If GPS is on, will instantiate TagActivity at the current location.
	 * RequestCode 1 corresponds to current location.
	 */
	private OnClickListener tagListener = new OnClickListener(){
		public void onClick(View view) {
			Location loc = locationManager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER);
			if (loc == null || !menuSettings.getBoolean("GPS_STATUS", true)){
				Toast.makeText(parent,"Please ensure GPS is on and has had time to get a location fix",Toast.LENGTH_SHORT).show();
			}
			else{
				currentLocationPoint = new GeoPoint((int)(loc.getLatitude() * 1000000.0), (int)(loc.getLongitude()*1000000.0));
				Intent myIntent = new Intent(view.getContext(), TaggingActivity.class);
				myIntent.putStringArrayListExtra("attributes", attributes);
				myIntent.putStringArrayListExtra("tagTypes",typeNames);
				startActivityForResult(myIntent, 1);
			}
		} 
	};
	/**
	 * The listener for the Menu Button.  Instantiates the MenuActivity.
	 */
	private OnClickListener menuListener = new OnClickListener(){
		public void onClick(View view) {
			Intent myIntent = new Intent(view.getContext(), MenuActivity.class);
			myIntent.putStringArrayListExtra("tagtypes",typeNames);
			startActivityForResult(myIntent, 0);
		}
	};
	/**
	 * The listener for the Refresh Button.  Adds pending tags to the server.
	 * If GPS is on, will get tags within the specified radius of the current location.
	 */
	private OnClickListener refreshListener = new OnClickListener(){
		public void onClick(View view){
			Location loc = locationManager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER);
			if (loc != null && menuSettings.getBoolean("GPS_STATUS",true)){
				currentLocationPoint = new GeoPoint((int)(loc.getLatitude() * 1000000.0), (int)(loc.getLongitude()*1000000.0));
				int longi = currentLocationPoint.getLongitudeE6();
				int lati = currentLocationPoint.getLatitudeE6();
				getTagsFromServer(longi, lati,radius);
			}
			//should have here an option to just get all tags for when their GPS is off.
			sendTagsToServer();
		}
	};
	/**
	 * Writes a Tag to the local file.  Should be removed if database is implemented.
	 * Tags are stored as strings, one to a line.
	 * @param tag The Tag to be written to the file.
	 */
	private void writeToFile(Tag tag){
		try{
			FileOutputStream writer = openFileOutput(TagFileName,Context.MODE_APPEND);
			//this requires extra characters because it does not synch up properly with the server format
			//this should be fixed when app storage is converted to database form.
			writer.write((tag.getInfoString(dateFormat)+"|\n").getBytes());
			writer.close();		
		}catch(Exception e){
			Log.v(ACTNAME,"Exception caught in writing to tagFile");
		} //the method is supposed to create the file, there *should* be no exception
	}
	/**
	 * Called when this Activity resumes.  This occurs when the app returns from another Activity,
	 * after onActivityResult.
	 * Turns the GPS back on if it is still on.
	 */
	protected void onResume(){
		super.onResume();
		if (menuSettings.getBoolean("GPS_STATUS",true)){
			Log.v(ACTNAME,"Resuming, GPS on");
			locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000,10,locationListener);
			myLocationOverlay.enableMyLocation();
		}
	}
	/**
	 * Called when this Activity pauses.  Turns off the GPS.
	 */
	protected void onPause(){
		locationManager.removeUpdates(locationListener);
		myLocationOverlay.disableMyLocation();
		super.onPause();
//		myLocationOverlay.disableCompass();
	}
	/**
	 * Called when an Activity created by TagItMapActivity returns from execution.  Called before onResume.
	 * RequestCode zero corresponds to MenuActivity.
	 * 		If returning from MenuActivity, this method reads the information from menuSettings and updates
	 * 		the GPS and tag filtering settings accordingly.
	 * RequestCode one corresponds to a tag created at the current location, two for a tag at press location.
	 * A tag has been created if resultCode is 1.  Otherwise, the user canceled the Tag.
	 * In either successful Tagging case, a new Tag is created, added to local memory, and to the local file.
	 */
	protected void onActivityResult(int requestCode, int resultCode,
			Intent data) {
		int tagTypeNum = menuSettings.getInt("COLOR_CHOICE",0);
		currentType = tagTypes.get(tagTypeNum);
		if (resultCode == RESULT_OK && requestCode == 0) {	
			if (!menuSettings.getBoolean("GPS_STATUS",true)){
				Log.v(ACTNAME,"Back from settings, GPS off");
				locationManager.removeUpdates(locationListener);
			}else{
				Log.v(ACTNAME,"Back from settings, GPS on");
				locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 10, locationListener);
				myLocationOverlay.enableMyLocation();
	//			myLocationOverlay.enableCompass();
			}
			for (TagType type : tagTypes){
	//			Log.v(ACTNAME,Boolean.toString(menuSettings.getBoolean(type.getName(),true)));
				if (menuSettings.getBoolean(type.getName(),true)){
					tagsOnScreen.filterInType(type);
				}
				else{
					tagsOnScreen.filterOutType(type);
				}
			}
			tagsOnScreen.filterList();
			mapView.invalidate();
		}
		if (requestCode == 1 || requestCode == 2){
			if (resultCode == 1){ 	//tag submitted
				String title = data.getStringExtra("title");
				String description = data.getStringExtra("description");
				final Tag tag;
				if (requestCode == 1){
					tag = new Tag(currentLocationPoint,title,description);
				}
				else{
					tag = new Tag(pressLocationPoint,title,description);
				}
				tag.setType(currentType);
				tag.setMarker(currentType.getMarker());
				String date = dateFormat.format(new Date());
				try {
					tag.setDate(dateFormat.parse(date));
					
				} catch (ParseException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				for (String attribute : attributes){
					tag.addAttribute(attribute, data.getStringExtra(attribute));
				}
				if(addOverlayItem(tag)){
					writeToFile(tag);
				} 
				
				tagsForServer.add(tag);
			}
		}
	}
	/**
	 * Calls through to the tagsList addOverlay method and invalidates the MapView.
	 * @param item the Tag to add.
	 * @return Whether or not the Tag was successfully added.  This would be false
	 * 		if the Tag was a duplicate.
	 */
	private boolean addOverlayItem(Tag item){
		boolean rVal = tagsOnScreen.addOverlay(item);
		mapView.invalidate(); 
		return rVal;
	}
	/**
	 * Calls through to the tagsList addOverlay method.  This method would be called multiple
	 * times in a row, then the calling method must invalidate the map.  This is used to improve efficiency
	 * when adding multiple Tags simultaneously.
	 * @param item the Tag to be added.
	 * @return Whether or not the Tag was successfully added.  This would be false
	 * 		if the Tag was a duplicate.
	 */
	private boolean addSeveralItems(Tag item){
		return tagsOnScreen.addOverlay(item);	
	}
	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}
	
	/**
	 * Initiates various Map attributes.
	 * Sets the inital zoom level to 15 and adds the Overlays to the Map.
	 */
	private void initMap(){
		mapView = (MapView) findViewById(R.id.GMap);	
		mapControl = mapView.getController();
		mapView.setBuiltInZoomControls(true);
		mapControl.setZoom(15);
		tagsOnScreen = new TagsList(currentType.getMarker(), this);
		mapOverlays = mapView.getOverlays();
		mapOverlays.add(tagsOnScreen);
		myLocationOverlay = new MyLocationOverlay(this, mapView);
		mapView.getOverlays().add(myLocationOverlay);
		mapView.getOverlays().add(new LongPressOverlay());
		mapView.invalidate();
	}
	/**
	 * 
	 * This class may be ineffective.  During testing on our tablet device,
	 * 		the onLocationChanged method appeared not to be being called.
	 * 		This led to the addition of specifying currentLocationPoint
	 * 		when the tag button is pressed. 
	 *
	 */
	private class myLocationListener implements LocationListener{
		@Override
		/**
		 * When the current location changes, change currentLocationPoint to the new Location.
		 * Also pops up a Toast saying so.  The Toast should probably be removed before publishing.
		 */
		public void onLocationChanged(Location loc) {
			if (loc != null) {
				//Geopoints in microdegrees latitude or longitude times 10^6
				currentLocationPoint = new GeoPoint((int)(loc.getLatitude() * 1000000.0), (int)(loc.getLongitude()*1000000.0));
				Toast.makeText(getBaseContext(), 
						"Location changed : Latitude: " + loc.getLatitude() + 
						" Longitude: " + loc.getLongitude(), 
						Toast.LENGTH_SHORT).show();
			}
		}
		/**
		 * Required for a class that implements LocationListener.  Does nothing.
		 */
		public void onProviderDisabled(String provider) {}
		/**
		 * Required for a class that implements LocationListener.  Does nothing.
		 */
		public void onProviderEnabled(String provider) {}
		/**
		 * Required for a class that implements LocationListener.  Does nothing.
		 */
		public void onStatusChanged(String provider, int status, Bundle extras) {}
	}
	
	/**
	 * 
	 * An Overlay that detects long presses.  When a long press is detected, calls createTagAtLocation.
	 * Currently, this class is too sensitive.  Even a tiny motion will cause it to miss the press.
	 *
	 */
	private class LongPressOverlay extends Overlay{
		/**
		 * A boolean indicating whether or not the user is currently pressing.  Not necessary,
		 * since the thread is interrupted by a detected non-long-press.
		 */
		boolean pressing;
		/**
		 * The number of milliseconds the user must press to be interpreted as a long press.
		 */
		int delayTime = 700;
		/**
		 * The thread to execute the waiting.  If it waits for delayTime and is not interrupted, it will
		 * then call tagThread, which creates a Tag at the press location.
		 */
		UIThreader waitThread = null;
		
		/**
		 * Called anytime the user touches the screen or moves their touch on the screen.
		 * Starts waitThread.  If waitThread is running, any new MotionEvent will interrupt it.
		 * 
		 * @param event the MotionEvent corresponding to what the user has done
		 * @param view	the MapView on which this Overlay is placed.
		 * @return	Returns false so that the touch will also be passed up to any other overlays or the map.
		 */
		public boolean onTouchEvent(final MotionEvent event, MapView view){
			/**
			 * A thread that simply creates a Tag at a Location.  Exists to interract with the parent's UI thread.
			 */
		Thread tagThread = new Thread(){
				public void run(){
					parent.createTagAtLocation(event);
				}
			};
			int action = event.getActionMasked();
			if (action == MotionEvent.ACTION_DOWN){
				if (waitThread != null){
					waitThread.interrupt();
				}
				pressing = true;
				waitThread = new UIThreader(tagThread){					
					public void run(){
						try{
							Thread.sleep(delayTime);
						}catch(InterruptedException e){
							return;
						}
						if(pressing){
							super.run();
						}
					}
				};
				waitThread.start();
			}
			else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_MOVE){
				waitThread.interrupt();
				pressing = false;
			}
			return false;
		}
	}
	/**
	 * A class used to run threads on the UIThread.  Necessary because Android
	 * requires any editting of a view to be done on the thread that created that view.
	 * Usage : Instantiate a UIThreader with the thread you wish to run on the UI Thread.  Then
	 * call the UIThreader's run when you wish it to be executed.
	 */
	class UIThreader extends Thread{
		/**
		 * The Runnable which will be executed on the UIThread when UIThreader's run is called.
		 */
		Runnable runner;
		/**
		 * Creates a UIThreader with the provided runnable as this object's runner
		 * @param runner The Runnable to be run when this object's run is called.
		 */
		public UIThreader(Runnable runner){
			this.runner=runner;
		}
		/**
		 * Executes the given Runnable on the UI Thread.
		 */
		public void run(){
			parent.runOnUiThread(runner);
		}
	}
}